﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace CMS6Tools.ConsoleApp.VS
{
    public class SlnFile
    {
        #region Ctor
        public SlnFile(string fileFullPath)
        {
            FileFullPath = fileFullPath;
            Projects = new List<ProjectInfo>();
            Global = new Global();
        }
        #endregion

        #region Properties
        public string VSFormatInfo { get; set; }
        public string VSVisualStudioVersion { get; set; }
        public List<ProjectInfo> Projects { get; set; }
        public Global Global { get; set; }
        public string FileFullPath { get; private set; }
        #endregion

        #region ToString
        public override string ToString()
        {
            var stringBuilder = new StringBuilder();
             
            stringBuilder.Append(VSFormatInfo);
             
            stringBuilder.Append(VSVisualStudioVersion);

            foreach (var projectInfo in Projects)
            {
                stringBuilder.Append(projectInfo.ToString()); 
            }

            stringBuilder.Append(Global.ToString());

            return stringBuilder.ToString();
        }
        #endregion

        #region Static Read
        public static SlnFile Read(string slnFileName)
        {
            var slnFileContent = string.Empty;
            using (var streamReader = new StreamReader(slnFileName))
            {
                slnFileContent = streamReader.ReadToEnd();
            }

            var endLine = "\r\n";

            var slnFile = new SlnFile(slnFileName);

            var hashTagIndex = slnFileContent.IndexOf("#");
            slnFile.VSFormatInfo = slnFileContent.Substring(0, hashTagIndex);
            slnFileContent = slnFileContent.Substring(hashTagIndex);

            var endLineIndex = slnFileContent.IndexOf(endLine);
            slnFile.VSVisualStudioVersion = slnFileContent.Substring(0, endLineIndex) + endLine;
            slnFileContent = slnFileContent.Substring(endLineIndex + endLine.Length);

            /*
            Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Components", "Components", "{020F9E78-4CDB-4AE3-A976-E4C67B4C7C5C}"
             * ProjectSection(ProjectDependencies) = postProject
		        {F080BE53-915B-49FB-9864-EDC91FC4CAE6} = {F080BE53-915B-49FB-9864-EDC91FC4CAE6}
	           EndProjectSection
            EndProject
            */
            var projectInfoRegex = "Project\\(\"(.*)\"\\)\\s*=\\s*\"(.*)\",\\s*\"(.*)\",\\s*\"(.*)\"\\s*\r\n([0-9a-z=\r\n\\s\"\\-\\{\\}\\(\\)\\.\\\\]*)EndProject\r\n";
            var projectInfoMatches = Regex.Matches(slnFileContent, projectInfoRegex, RegexOptions.IgnoreCase | RegexOptions.Multiline);
            foreach (Match m in projectInfoMatches)
            {
                var projectInfo = new ProjectInfo
                {
                    TypeGUID = m.Groups[1].Value,
                    Name = m.Groups[2].Value,
                    Path = m.Groups[3].Value,
                    GUID = m.Groups[4].Value,
                    Content = m.Groups[5].Value.Trim()
                };
                slnFile.Projects.Add(projectInfo);
            } 

            var sectionInfoRegex = "GlobalSection\\((.*)\\)\\s*=\\s*(.*)\r\n([^\\(\\)]*)EndGlobalSection\r\n";
            var sectionInfoMatches = Regex.Matches(slnFileContent, sectionInfoRegex, RegexOptions.IgnoreCase | RegexOptions.Multiline);
            foreach (Match m in sectionInfoMatches)
            {
                var section = GlobalSection.Create(type: m.Groups[1].Value, condition: m.Groups[2].Value, content: m.Groups[3].Value);
                slnFile.Global.Sections.Add(section);
            }  

            return slnFile;
        }
        #endregion
    }

    public class ProjectInfo
    {
        public const string TypeGUIDs_Folder = "{2150E333-8FDC-42A3-9474-1A3956D46DE8}";
        public const string TypeGUIDs_Project = "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}";

        public string TypeGUID { get; set; }
        public string Name { get; set; }
        public string Path { get; set; }
        public string GUID { get; set; }
        public string Content { get; set; }
        
        public override string ToString()
        {
            var stringBuilder = new StringBuilder();

            stringBuilder.AppendLine(string.Format("Project(\"{0}\") = \"{1}\", \"{2}\", \"{3}\"", TypeGUID, Name, Path, GUID)); 
             
            stringBuilder.Append(Content);

            stringBuilder.AppendLine("EndProject");

            return stringBuilder.ToString();
        }
    }

    public class GlobalSection
    {
        public const string Types_ProjectConfigurationPlatforms = "ProjectConfigurationPlatforms";
        public const string Types_TeamFoundationVersionControl = "TeamFoundationVersionControl";
        public const string Types_NestedProjects = "NestedProjects";

        public string Type { get; set; }

        public string Condition { get; set; }

        public string Content { get; set; }

        protected virtual string GetContentStr()
        {
            return Content;
        }

        public override string ToString()
        {
            var stringBuilder = new StringBuilder();

            stringBuilder.AppendLine(string.Format("\t\tGlobalSection({0}) = {1}", Type, Condition));

            stringBuilder.Append(GetContentStr());

            stringBuilder.AppendLine("\t\tEndGlobalSection"); 

            return stringBuilder.ToString();
        }

        public static GlobalSection Create(string type, string condition, string content)
        {
            switch (type)
            {
                case Types_NestedProjects:
                    return new NestedProjectsGlobalSection(content)
                    {
                        Type = type,
                        Condition = condition,
                    };
                case Types_ProjectConfigurationPlatforms:
                    return new ProjectConfigurationGlobalSection(content)
                    {
                        Type = type,
                        Condition = condition,
                    };
                case Types_TeamFoundationVersionControl:
                default:
                    return new GlobalSection
                    {
                        Type = type,
                        Condition = condition,
                        Content = content
                    };
            }
        }
    }

    public class ProjectConfigurationGlobalSection : GlobalSection
    {
        public ProjectConfigurationGlobalSection(string content)
        {
            Content = content;

            Projects = new List<ProjectConfigurationProjectInfo>();

            var regex = "([^\\.]*)\\.(.*)\\s*=\\s*(.*)\r\n";
            var matches = Regex.Matches(Content, regex, RegexOptions.IgnoreCase | RegexOptions.Multiline);
            foreach (Match match in matches)
            {
                Projects.Add(new ProjectConfigurationProjectInfo
                {
                    Guid = match.Groups[1].Value.Trim(),
                    Type = match.Groups[2].Value.Trim(),
                    Value = match.Groups[3].Value.Trim()
                });
            }
        }

        public List<ProjectConfigurationProjectInfo> Projects { get; set; }

        protected override string GetContentStr()
        {
            var stringBuilder = new StringBuilder();
            foreach (var project in Projects)
            {
                stringBuilder.AppendLine(project.ToString());
            }
            return stringBuilder.ToString();
        }
    }

    public class ProjectConfigurationProjectInfo
    {
        public string Guid { get; set; }
        public string Type { get; set; }
        public string Value { get; set; }

        public override string ToString()
        {
            return string.Format("\t\t\t{0}.{1} = {2}", Guid, Type, Value);
        }
    }

    public class NestedProjectsGlobalSection : GlobalSection
    {
        public NestedProjectsGlobalSection(string content)
        {
            Content = content;

            Projects = new List<NestedProjectInfo>();

            var regex = "(.*)\\s*=\\s*(.*)\r\n";
            var matches = Regex.Matches(Content, regex, RegexOptions.IgnoreCase | RegexOptions.Multiline);
            foreach (Match match in matches)
            {
                Projects.Add(new NestedProjectInfo
                {
                    ChildGuid = match.Groups[1].Value.Trim(),
                    ParentGuid = match.Groups[2].Value.Trim()
                });
            }
        }

        public List<NestedProjectInfo> Projects { get; set; }

        protected override string GetContentStr()
        {
            var stringBuilder = new StringBuilder();
            foreach (var project in Projects)
            {
                stringBuilder.AppendLine(project.ToString());
            }
            return stringBuilder.ToString();
        }
    }

    public class NestedProjectInfo
    {
        public string ChildGuid { get; set; }
        public string ParentGuid { get; set; }

        public override string ToString()
        {
            return string.Format("\t\t\t{0} = {1}", ChildGuid, ParentGuid);
        }
    }

    public class Global
    {
        public Global()
        {
            Sections = new List<GlobalSection>();
        }

        public List<GlobalSection> Sections { get; private set; }

        public override string ToString()
        {
            var stringBuilder = new StringBuilder();

            stringBuilder.AppendLine("Global"); 

            foreach (var section in Sections)
            {
                stringBuilder.Append(section.ToString());
            }

            stringBuilder.AppendLine("EndGlobal"); 

            return stringBuilder.ToString();
        }
    }
}
